#include <iostream>
#include <thread>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
#include <ctime>
#include <cassert>
#include <cstdio>
using namespace std;

// pthread_mutex_t 是原生线程库提供的一个数据类型
// pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 全局锁的初始化方式

/*
#define PTHREAD_MUTEX_INITIALIZER
{
    {
        0, 0, 0, 0, 0, __PTHREAD_SPINS, { 0, 0 }
    }
}
*/

class ThreadInfo
{
public:
    ThreadInfo(const std::string &threadName, pthread_mutex_t *pmtx)
        : _threadName(threadName), _pmtx(pmtx)
    {
    }

public:
    std::string _threadName;
    pthread_mutex_t *_pmtx;
};

int tickets = 10000; // 并发访问如果不加保护的访问临界资源就会出现数据不一致
void *getTickets(void *args)
{
    // 加锁的粒度越小越好
    ThreadInfo *td = (ThreadInfo *)args;
    int n = 0;
    while (true)
    {
        // 抢票逻辑
        n = pthread_mutex_lock(td->_pmtx);
        assert(n == 0);

        // 临界区
        if (tickets > 0) // 1. 逻辑运算
        {
            // 模拟抢票业务
            usleep(rand() % 1500);

            tickets--; // 2. 算术运算
            cout << td->_threadName << "have got one, remain: " << tickets << endl;

            n = pthread_mutex_unlock(td->_pmtx);
            assert(n == 0);
        }
        else
        {
            n = pthread_mutex_unlock(td->_pmtx);
            assert(n == 0);

            break;
        }

        // 抢完票后续动作
        usleep(rand() % 2000);
    }
    delete td;

    return nullptr;
}

#define THREAD_NUM 100
int main()
{
    time_t start = time(nullptr);

    srand((unsigned long)time(nullptr) ^ getpid() ^ 0x147);

    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx, nullptr);

    pthread_t t[THREAD_NUM];

    // 多线程抢票的逻辑
    for (int i = 0; i < THREAD_NUM; i++)
    {
        std::string name = "thread " + std::to_string(i + 1);
        ThreadInfo *td = new ThreadInfo(name, &mtx);
        pthread_create(t + i, nullptr, getTickets, (void *)td);
    }

    for (int i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(t[i], nullptr);
    }

    pthread_mutex_destroy(&mtx);

    time_t end = time(nullptr); // typedef long time_t

    std::cout << "run time: " << (long long)(end - start) << "s" << std::endl;
}
